/*
--------------------------------------------------------------------------------
				Ak-Lib. 
			 Project       	: Ak-Lib
			 Group         	: Ak-Lib R&D Team
			 Author        	: Ocean.Wang
			 Create Date 	: 05-01-2010
--------------------------------------------------------------------------------
Code Name 	:  A.K 	~v~
Description	:  Yet Another Cross-Platform Lib.
Cautions    	:
--------------------------------------------------------------------------------
Change History:
Version		: 0.1
Building 		: 05/01/10
10-05.01 first draft, build, debug and test.
--------------------------------------------------------------------------------
*/

#include "ak_jobq.h"

/* Trace Lvl */
static 	int 	_gTraceLvl_SJobQ = TRACELVL_NORMAL;

int 	SJobQ_SetTraceLvl(int lvl)
{
	int 	ret = -1;
	
	switch(lvl){
		case TRACELVL_FATAL:
		case TRACELVL_ERROR:
		case TRACELVL_WARNING:
		case TRACELVL_NORMAL:
		case TRACELVL_DEBUG:
		case TRACELVL_ALL:
			_gTraceLvl_SJobQ = lvl;
			ret = 0;
			break;
		default:
			break;
	}

	return ret;
}

int 	SJobQ_GetTraceLvl(void)
{
	return  _gTraceLvl_SJobQ;
}

int 	SJobQ_Init(_t_sysJobQ * pJobQ, 
					_t_sysSemB * pWake, 
					_t_cbentry * jProc,
					unsigned int ringCnt, 
					unsigned int batch, 
					unsigned int jobCnt, 
					unsigned int jobSz)
{
	_t_sysMPool 	* pPool;
	_t_sysMHeap 	* pHeap;
	_t_sysMSlot 	* pSlot;
	unsigned int 	mLimit;
	int 			ret;

	ret = -1;
	if(pJobQ && pWake && jProc && (jobSz >= sizeof(_t_sysJobNode))){
		if(ringCnt < (SJOBQ_GAP * 2)){
			ringCnt = (SJOBQ_GAP * 2);
		}
		/* Init the Ring */
		pJobQ->ringJob = (_t_sysJobNode **)SYS_CALLOC(ringCnt, sizeof(_t_sysJobNode *));
		if(pJobQ->ringJob){
			pJobQ->ringCnt = ringCnt;
			pJobQ->rdIx = 0;
			pJobQ->wrIx = 0;
			pJobQ->jStat= 0;
		 	SYS_INIT_CS(&(pJobQ->ringLock));

			if(jobCnt < (ringCnt)){
				jobCnt = (ringCnt);
			}
			/* Init the Fixed Pool */
			pHeap = &(pJobQ->mHeap);
			pPool = &(pJobQ->mPool);
			/* Using the 1st Slot */
			pSlot = pJobQ->slots;

			/* Init the MLimit */
			mLimit = (jobSz * jobCnt);
			MHeap_InitEx(pHeap, mLimit);
			MPool_Init(pPool, pHeap,	
						mLimit, 
						jobSz, 
						1,	 1, MPOOL_T_SIMP);
			/* Init the Slot */
			MPOOL_SET_SLOT(pPool, pSlot);
			MSlot_Init(pSlot, pPool, 0, jobSz);
			
			/* Set the Magic */
			pJobQ->magic = SJOBQ_MAGIC;
			pJobQ->wake  = pWake;
			/* Calc the Batch */
			if(batch > SJOBQ_BAT_MAX){
				batch = SJOBQ_BAT_MAX;
			}
			pJobQ->batch = batch;
			_CBENTRY_ASSIGN(&(pJobQ->jProc), jProc);
			ret = 0;
		}
	}
	return ret;
}


_t_sysJobNode * SJobQ_NodeAlloc(_t_sysJobQ * pJobQ)
{
	_t_sysJobNode * pJobNode;
	pJobNode = NULL;
	if(pJobQ && (pJobQ->magic == SJOBQ_MAGIC)){
		pJobNode = (_t_sysJobNode *)MPool_AllocSi(&(pJobQ->mPool));
	}
	return pJobNode;
}

int 	SJobQ_NodeFree(_t_sysJobQ * pJobQ, _t_sysJobNode * pJobNode)
{
	int 	ret;
	ret = -1;
	if(pJobQ && (pJobQ->magic == SJOBQ_MAGIC) && pJobNode){
		MPool_FreeSi(&(pJobQ->mPool), pJobNode);
		ret = 0;
	}
	return ret;
}

static 	int 	_SJobQ_NodeFreeQ(_t_sysJobQ * pJobQ, _t_blist * pQue)
{
	int 	ret;
	ret = -1;
	if(pJobQ && (pJobQ->magic == SJOBQ_MAGIC) && pQue && (pQue->count > 0)){
		MPool_FreeNi(&(pJobQ->mPool), pQue);
		ret = 0;
	}
	return ret;
}

int		SJobQ_Sche(_t_sysJobQ * pJobQ, _t_sysJobNode ** nodeA)
{
	int 				i, count;
	unsigned int 		batch;
	_t_blist 			jQue;
	_t_sysJobNode ** 	ppJobNode;
	_t_cbentry 	  *		jProc;
	
	count = 0;
	batch = pJobQ->batch;
	if((batch > 0) && (!SJOBQ_ISEMPTY(pJobQ))){
		count = SJOBQ_COUNT(pJobQ);
		SYS_TRACE(_gTraceLvl_SJobQ, TRACELVL_DEBUG, "Sche(%p): %-4d -> %-4u \n", 
					pJobQ, count, batch); 
		
		if(count > batch){
			count = batch;
		}

		/* Add Statistics */
		if(pJobQ->jStat < count){
			pJobQ->jStat = count;
		}
		
		/* Retrive the JobNode */
		ppJobNode = nodeA;
		for(i=0; i<count; i++, ppJobNode++){
			SJOBQ_RING_GET(pJobQ, *ppJobNode);
		}
		
		/* Process */
		BLIST_INIT(&jQue);	
		ppJobNode= nodeA;
		jProc 	 = &(pJobQ->jProc);
		for(i = 0; i < count; i++, ppJobNode++){
			jProc->cbProc(jProc->pArg, jProc->lArg, *ppJobNode);
			BLST_ADD2T(&jQue, &((*ppJobNode)->mi.aNode));
		}
		/* Free them All */
		_SJobQ_NodeFreeQ(pJobQ, &jQue);
	}
	return count;
}


/* Here Adding the Support for Slab Allocator */
#define _JOBQ_SLAB_MEM_PAGE 			0X80000		/*  512K Based */
#define _JOBQ_SLAB_MEM_MIN 				256

int 	SJobQ_InitEx(_t_sysJobQ * pJobQ, 
					_t_sysSemB 	* pWake, 
					_t_cbentry 	* jProc,
					unsigned int ringCnt, 
					unsigned int batch, 
					unsigned int jobCnt, 
					unsigned int jobSz)
{
	_t_sysMPool 	* pPool;
	_t_sysMHeap 	* pHeap;
	_t_sysMSlot 	* pSlot;
	size_t 	   		* pSlab;
	unsigned int 	mLimit, itSz, slabCnt;
	int 			i, ret;

	ret = -1;
	if(	pJobQ && pWake && jProc && 
		(jobSz >= sizeof(_t_sysJobNode)) && 
		(jobSz <= (_JOBQ_SLAB_MEM_PAGE/2))){
		if(ringCnt < (SJOBQ_GAP * 2)){
			ringCnt = (SJOBQ_GAP * 2);
		}
		/* Init the Ring */
		pJobQ->ringJob = (_t_sysJobNode **)SYS_CALLOC(ringCnt, sizeof(_t_sysJobNode *));
		if(pJobQ->ringJob){
			pJobQ->ringCnt = ringCnt;
			pJobQ->rdIx = 0;
			pJobQ->wrIx = 0;
			pJobQ->jStat= 0;
		 	SYS_INIT_CS(&(pJobQ->ringLock));

			if(jobCnt < (ringCnt)){
				jobCnt = (ringCnt);
			}

			/* Init the Slab Pool */
			pHeap = &(pJobQ->mHeap);
			pPool = &(pJobQ->mPool);
			pSlot = pJobQ->slots;
			pSlab = pJobQ->slabs;

			/* Calc the Slab Cnt*/
			MSlab_CalcCnt(_JOBQ_SLAB_MEM_MIN, jobSz, &slabCnt, &itSz);
			
			/*  Calc the Page Limit */
			mLimit 	= (itSz + sizeof(_t_sysJobNode))* jobCnt;
			/* We Judge the Slab & MEM_PAGE */
			if(mLimit < (slabCnt * _JOBQ_SLAB_MEM_PAGE)){
				mLimit = slabCnt * _JOBQ_SLAB_MEM_PAGE;
			}
			mLimit  /= MPOOL_PAGESZ;
			mLimit  += 1;

			MHeap_Init(pHeap, mLimit);
			MPool_Init(pPool, pHeap,	_JOBQ_SLAB_MEM_PAGE,  	
						itSz,		
						1,	(mLimit * MPOOL_PAGESZ)/_JOBQ_SLAB_MEM_PAGE, 	MPOOL_T_SLAB_GC);
			MSlab_Init(pPool, pSlot,  	pSlab,	slabCnt, 		_JOBQ_SLAB_MEM_MIN,		sizeof(_t_sysJobNode));
			/* Prepare */
			for(i=0; i<slabCnt; i++){ 
				MPool_Pre(pPool, i, 1);
			}
			
			/* Set the Magic */
			pJobQ->magic = SJOBQ_MAGIC;
			pJobQ->wake  = pWake;
			/* Calc the Batch */
			if(batch > SJOBQ_BAT_MAX){
				batch = SJOBQ_BAT_MAX;
			}
			pJobQ->batch = batch;
			_CBENTRY_ASSIGN(&(pJobQ->jProc), jProc);
			ret = 0;
		}
	}
	return ret;
}

_t_sysJobNode * SJobQ_NodeAllocEx(_t_sysJobQ * pJobQ, size_t size)
{
	_t_sysJobNode * pJobNode;
	pJobNode = NULL;
	if(pJobQ && (pJobQ->magic == SJOBQ_MAGIC)){
		pJobNode = (_t_sysJobNode *)MPool_AllocEx(&(pJobQ->mPool), size);
		if(pJobNode == NULL){
			/* Let's Try GC */
			MPool_GC(&(pJobQ->mPool), 1);
			/* Try Again */
			pJobNode = (_t_sysJobNode *)MPool_AllocEx(&(pJobQ->mPool), size);	
		}
	}
	return pJobNode;
}

int 	SJobQ_NodeFreeEx(_t_sysJobQ * pJobQ, _t_sysJobNode * pJobNode)
{
	int 	ret;
	ret = -1;
	if(pJobQ && (pJobQ->magic == SJOBQ_MAGIC) && pJobNode){
		MPool_FreeEx(&(pJobQ->mPool), &(pJobNode->mi));
		ret = 0;
	}
	return ret;
}

int		SJobQ_ScheEx(_t_sysJobQ * pJobQ, _t_sysJobNode ** nodeA)
{
	int 				i, count;
	unsigned int 		batch;
	_t_sysJobNode ** 	ppJobNode;
	_t_cbentry 	  *		jProc;
	
	count = 0;
	batch = pJobQ->batch;
	if((batch > 0) && (!SJOBQ_ISEMPTY(pJobQ))){
		count = SJOBQ_COUNT(pJobQ);
		SYS_TRACE(_gTraceLvl_SJobQ, TRACELVL_DEBUG, "ScheEx(%p): %-4d -> %-4u \n", 
					pJobQ, count, batch); 
		
		if(count > batch){
			count = batch;
		}

		/* Add Statistics */
		if(pJobQ->jStat < count){
			pJobQ->jStat = count;
		}
		
		/* Retrive the JobNode */
		ppJobNode = nodeA;
		for(i=0; i<count; i++, ppJobNode++){
			SJOBQ_RING_GET(pJobQ, *ppJobNode);
		}
		
		/* Process  & Free */
		ppJobNode= nodeA;
		jProc 	 = &(pJobQ->jProc);
		for(i = 0; i < count; i++, ppJobNode++){
			jProc->cbProc(jProc->pArg, jProc->lArg, *ppJobNode);
			SJobQ_NodeFreeEx(pJobQ, *ppJobNode);
		}
		
	}
	return count;
}


int 			SJobQ_Dump(_t_sysJobQ * pJobQ, char * outBuf, int bufLen)
{
	int len = 0;

	if(pJobQ && outBuf && (bufLen > 0)){
		/* Sche Dump */
		len += SYS_SNPRINTF(outBuf + len, bufLen -len, "(JobQ: Ring:%-4u - Bat:%-4u - Rd:%-3u - Wr:%-3u):\n",
			pJobQ->ringCnt, pJobQ->batch, pJobQ->rdIx, pJobQ->wrIx);
		
		/* Mem Dump */
		len += MPool_Dump(&(pJobQ->mPool), outBuf + len, bufLen - len);
	}

	return len;
}

